package web.controller;

import common.consts.Consts;
import common.consts.Header;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import service.ServiceResponse;
import web.Registry;
import web.security.JWT;
import web.security.JwtClaim;
import web.security.Protection;
import web.utils.RequestHolder;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by aceyin on 15/7/4.
 */
public abstract class Base {
    private static final Logger LOGGER = LoggerFactory.getLogger(Base.class);

    /**
     * 每个Controller初始化的时候，自动把自己加入到Registry，以便AuthenticationFilter可以工作。
     */
    public Base() {
        Class clazz = getClass();

        Annotation classLevelRequestMapping = clazz.getAnnotation(RequestMapping.class);
        if (classLevelRequestMapping == null) {
            return;
        }
        RequestMapping rm = (RequestMapping) classLevelRequestMapping;

        String[] topUrls = rm.value();

        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            // only public method
            RequestMapping mapping = AnnotationUtils.getAnnotation(method, RequestMapping.class);
            Protection protection = AnnotationUtils.getAnnotation(method, Protection.class);
            if (mapping == null || protection == null) {
                continue;
            }
            String[] methodUrls = mapping.value();
            List<String> methodList = extractRequestMethods(mapping);
            List<String> totalUrls = concatTotalUrlList(topUrls, methodUrls);
            if (totalUrls.size() <= 0) {
                continue;
            }

            for (String m : methodList) {
                for (String u : totalUrls) {
                    if (!u.startsWith(Consts.API_ROOT_URI)) {
                        Registry.add(m + ":" + Consts.API_ROOT_URI + u);
                    } else {
                        Registry.add(m + ":" + u);
                    }
                }
            }
        }
    }

    private List<String> concatTotalUrlList(String[] topUrls, String[] methodUrls) {
        List<String> result = new ArrayList<>();

        boolean emptyTopUrl = topUrls == null || topUrls.length <= 0;
        boolean emptyMethodUrl = methodUrls == null || methodUrls.length <= 0;
        // both urls are empty
        if (emptyTopUrl && emptyMethodUrl) {
            return result;
        }
        // only class urls are empty
        if (emptyTopUrl && !emptyMethodUrl) {
            for (String u : methodUrls) {
                result.add(fixUrl(u));
            }
            return result;
        }

        // only method urls are empty
        if (emptyMethodUrl && !emptyTopUrl) {
            for (String u : topUrls) {
                result.add(fixUrl(u));
            }
            return result;
        }

        // both urls are not empty
        for (String top : topUrls) {
            String u1 = fixUrl(top);
            for (String m : methodUrls) {
                String u2 = fixUrl(m);
                result.add(u1 + u2);
            }
        }
        return result;
    }

    private List<String> extractRequestMethods(RequestMapping mapping) {
        List<String> methodList = new ArrayList<>();
        RequestMethod[] requestMethods = mapping.method();

        if (requestMethods == null) {
            methodList.add("GET");
        } else {
            for (RequestMethod m : requestMethods) {
                methodList.add(m.name());
            }
        }
        return methodList;
    }

    private String fixUrl(String s) {
        return Consts.API_ROOT_URI + (s.startsWith("/") ? s : "/" + s);
    }

    protected ServiceResponse fail(String msg) {
        return ServiceResponse.fail(msg);
    }

    protected long getUserIdFromAuthHeader() {
        String header = RequestHolder.RAW_REQUEST.get().getHeader(Header.AUTHORIZATION);
        JwtClaim claim = JWT.getIdentity(header);
        return claim.getUserid();
    }

    /**
     * Copy data from source object to target class.
     *
     * @param source
     * @param target
     * @param <T>
     * @return
     */
    protected <T> T copy(Object source, Class<T> target) {
        try {
            T t = target.newInstance();
            BeanUtils.copyProperties(t, source);
            return t;
        } catch (Exception e) {
            LOGGER.error("Error while copy data from {} to {}", source.getClass().getName(), target.getName());
        }
        return null;
    }
}
